package com.guonl.aspect;

import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.guonl.exception.BusinessException;
import com.guonl.open.api.config.OpenApiProperties;
import com.guonl.utils.SignatureUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.List;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;


@Slf4j
@Aspect
@Component
public class SignAspect {

    @Autowired
    private OpenApiProperties openApiProperties;

    private static final String KEY = "KEY";
    private static final String SIGN = "SIGN";
    private static final String TIMESTAMP = "Timestamp";

    @Before("@annotation(com.guonl.annotations.SignatureValidation)")
    public void doBefore() throws Throwable {


        // 接收到请求，记录请求内容
        HttpServletRequest request = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest();

        String apiKey = request.getHeader(KEY);
        String sign = request.getHeader(SIGN);
        String timestamp = request.getHeader(TIMESTAMP);

        // 1、获取请求sign签名参数，
        if (StringUtils.isBlank(sign)) {
            throw new BusinessException("签名参数不能为空");
        }
        // 2、获取请求参数secret
        if (StringUtils.isBlank(apiKey)) {
            throw new BusinessException("apiKey不能为空");
        }
        // 3、获取请求参数timestamp 时间戳
        if (StringUtils.isBlank(timestamp)) {
            throw new BusinessException("timestamp不能为空");
        }
        //4. 比较时间，60s内为合法请求
        if (Math.abs(Long.parseLong(timestamp) - System.currentTimeMillis()) > 60000) {
            throw new BusinessException("timestamp已过期");
        }

        String apiSecret = getApiSecret(apiKey);
        SortedMap<String, Object> allParams = getAllParams(request);
        String queryjsonStr = JSON.toJSONString(allParams);
        String mySign = SignatureUtil.generateSign(apiSecret, request.getMethod(), request.getRequestURI(), queryjsonStr, timestamp);

        if (!StrUtil.equals(sign, mySign)) {
            log.info("签名验证-请求参数信息:{}", queryjsonStr);
            log.error("签名验证失败,外部签名:{},内部签名:{}", sign, mySign);
            throw new BusinessException("签名校验失败");
        }
    }

    public String getApiSecret(String apiKey) {
        List<OpenApiProperties.ApiKeyInfo> apiKeyList = openApiProperties.getApiKeyList();
        return apiKeyList.stream().filter(x -> x.getApiKey().equals(apiKey)).findFirst().get().getApiSecret();
    }

    private String createSign(String apiSecret, String signatureString) {
        String signature = "";
        try {
            Mac hmacSha512 = Mac.getInstance("HmacSHA512");
            SecretKeySpec spec = new SecretKeySpec(apiSecret.getBytes(), "HmacSHA512");
            hmacSha512.init(spec);
            signature = Hex.encodeHexString(hmacSha512.doFinal(signatureString.getBytes()));
        } catch (Exception e) {
            log.error("生成签名失败", e);
            throw new BusinessException("生成签名失败");
        }
        return signature;
    }

    /**
     * 将URL的参数和body参数合并
     */
    private SortedMap<String, Object> getAllParams(HttpServletRequest request) throws IOException {
        SortedMap<String, Object> result = new TreeMap<>();
        // 获取URL上的参数
        getUrlParams(request, result);
        // 获取body参数
        getAllRequestParam(request, result);
        return result;
    }

    /**
     * 获取url参数
     */
    private void getUrlParams(HttpServletRequest request, SortedMap<String, Object> result) {
        String param = "";
        try {
            String urlParam = request.getQueryString();
            if (urlParam != null) {
                param = URLDecoder.decode(urlParam, "utf-8");
            }
        } catch (UnsupportedEncodingException e) {
            log.error("获取url参数失败", e);
            throw new BusinessException("生成签名失败");
        }
        String[] params = param.split("&");
        for (String s : params) {
            int index = s.indexOf("=");
            if (index != -1) {
                result.put(s.substring(0, index), s.substring(index + 1));
            }
        }
    }

    /**
     * 获取 Body 参数
     */
    private void getAllRequestParam(final HttpServletRequest request, SortedMap<String, Object> result) throws IOException {
        BufferedReader reader = new BufferedReader(new InputStreamReader(request.getInputStream()));
        String str = "";
        StringBuilder wholeStr = new StringBuilder();
        while ((str = reader.readLine()) != null) {
            wholeStr.append(str);
        }
        wholeStr.trimToSize();
        String body = wholeStr.toString();
        if (StringUtils.isNotBlank(body)) {
            Map<String, Object> map = JSON.parseObject(body, new TypeReference<Map<String, Object>>() {
            });
            result.putAll(map);
        }
    }


}

